[스프링] 스프링 (IOC) 컨테이너

업데이트:



✅ 스프링 IOC 컨테이너

스프링 컨테이너는 Bean들의 생명주기를 관리하는 핵심 컴포넌트이다. Bean의 생성, 관리, 제거 등의 역할을 담당하며 IOC개념이 적용된 대표적인 기능이다.

스프링 컨테이너는 DI가 이루어진 빈들을 BeanFactoryApplicationContext라는 2개의 컨테이너로 제어하고 관리한다.

스프링 컨테이너는 XML, @Annotation 기반의 자바 설정정보 클래스로 만들수 있는데, 스프링 부트를 사용하기 전에는 xml을 통해 직접 설정해줘야 했지만 스프링 부트가 등장한 현재 대부분 사용하지 않는다. 그러나 스프링 컨테이너가 어떻게 구성되고 동작하는지를 알고 사용하는것과 모르고 사용하는것은 천지차이 이므로 반드시 알고 넘어가자.

참고 ) “스프링 컨테이너”라는 용어는 주로 ApplicationContext 인터페이스를 구현한 구체적인 컨테이너 클래스를 지칭하는 경우가 많습니다. 그러나 스프링 프레임워크에서는 ApplicationContext를 확장한 다양한 컨테이너 구현체를 제공하고 있습니다.

  • AnnotationConfigApplicationContext
  • ClassPathXmlApplicationContext
  • FileSystemXmlApplicationContext
  • XmlWebApplicationContext



📌 스프링 컨테이너를 왜 사용할까 ?

스프링 컨테이너는 빈의 생명주기를 관리한다고 했다. 생명주기 관리는 빈의 생성,제거 등을 의미하고 이를 관리한다는 것은 빈을 사용하는 주체 대신 빈에 설정정보를 대입하여 생성 및 제거를 대신한다는 것을 의미한다. IOC가 적용된 개념이라고 볼 수 있다.

기존의 자바코드에서는 객체(Bean)를 사용하기 위해서는 new라는 생성자를 사용했다. 이후 객체끼리 참조를 반복하여 객체간의 의존성이 높아지게 된다. 그러나 참조가 심해지면 객체간의 의존성이 지나치게 높아져 좋은 객체 지향 설계 원칙에 위배된다.

이러한 기존 자바의 단점을 보완하기 위해 탄생하게 된것이 Spring이고, 이때 객체 간의 의존성을 낮추기 위해 사용하게 된것이 바로 스프링 컨테이너 이다. 이를 정리하면 다음과 같다.

  1. 객체의 생명주기 관리: 스프링 컨테이너는 객체의 생성, 초기화, 소멸 등의 생명주기를 관리합니다. 개발자는 객체의 생성과 소멸에 대한 로직을 신경쓰지 않고, 단순히 컨테이너에게 객체 생성을 요청함으로써 효율적으로 객체를 관리할 수 있습니다.

  2. 의존성 주입(Dependency Injection): 스프링 컨테이너는 의존성 주입을 통해 객체 간의 의존성을 해결합니다. 의존성 주입은 객체가 다른 객체에 의존할 때 직접 객체를 생성하거나 참조하는 것이 아니라, 컨테이너가 객체 간의 의존성을 자동으로 연결해줌으로써 개발자가 객체 간의 결합도를 낮출 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성을 향상시킬 수 있습니다.

  3. 관심사의 분리: 스프링 컨테이너는 객체 생성과 의존성 주입을 담당하므로, 개발자는 비즈니스 로직에 집중할 수 있습니다. 컨테이너는 애플리케이션의 구성 요소들을 중앙에서 관리하기 때문에, 객체 간의 관계와 설정 정보를 한 곳에서 관리할 수 있습니다.





✅ 스프링 컨테이너의 구성

스프링컨테이너의 구성을 보기 앞서 ApplicationContext와 BeanFactory가 어떤 역할을 하는지 간단하게 살펴보자.

  • BeanFactory - 모든 유형의 개체를 관리할 수 있는 구성 프레임워크 및 기본기능 제공
  • ApplicationContext - BeanFactory를 확장하고 Spring AOP와의 통합, 메시지 리소스 처리 및 이벤트 게시와 같은 다양한 엔터프라이즈 특정 기능 추가

앞서 스프링 컨테이너는 ApplicationContext와 BeanFactory로 이루어졌다고 언급했는데, 사실 ApplicationContext는 BeanFactory를 상속하고 있는 구조를 띈다. 뿐만 아니라 여러 인터페이스를 다중상속 하고 있는데 각 기능을 살펴보면 다음과 같다.

  • MessageSource : 다국어 메시지 처리 기능 제공
  • EnvironmentCapable : 프로파일, 프로퍼티 기능 제공
  • BeanFactory : 스프링 빈을 관리하고 조회하는 역할 담당
  • ApplicationEventPublisher : 이벤트 기반 프로그래밍을 할때 필요한 기능 제공
  • ResourceLoader : 리소스를 읽어오는 기능 제공

때문에 ApplicationContext는 Bean을 관리하는 기능 뿐만 아니라 다국어 메시지처리, 리소스, 이벤트와 관련된 기능을 전부 가지고 있는 인터페이스이다.

다시 정리하면 스프링 컨테이너는 크게보면 ApplicationContext 인터페이스를 구현한 구체적인 컨테이너 클래스를 가리키고, 일반적으로는 BeanFactory 인터페이스를 구현하여 빈을 관리하는 컨테이너 클래스를 의미한다.


빈 팩토리 : 빈을 생성하고 관계를 설정하는 IoC의 기본 기능에 초점

애플리케이션 컨텍스트 : 애플리케이션 전반에 걸쳐 모든 구성요소의 제어를 담당하는 IoC 엔진





✅ 스프링 컨테이너 구현

스프링 컨테이너를 코드로 구현하는 방식은 크게 두가지가 있다.

  1. @Annotation을 사용한 구현
  2. XML 파일을 사용한 구현

두가지 경우 모두 예제를 통해 살펴보겠다.



📌 @Annotation을 활용한 스프링 컨테이너 구현


@Configuration  // 설정정보 라는 표시
public class DaoFactory {

	@Bean // 오브젝트 생성을 담당하는 IoC용 메소드 라는 표시
	public UserDao userDao() {
		UserDao userDao = new UserDao();
		userDao.setDataSource(dataSource());
		return userDao();
	}

	@Bean // 오브젝트 생성을 담당하는 IoC용 메소드 라는 표시
	public DataSource dataSource() {
		SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
		
		dataSource.setDriverClass(com.mysql.jdbc.Driver.class);
		dataSource.setUrl("jdbc:mysql://localhost/user");
		dataSource.setUsername("root");
		dataSource.setPassword("root");
		
		return dataSource;
	}
}


public class UserDaoTest {
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		ApplicationContext context = new AnnotationConfigApplicationContext(DaoFactory.class);
		UserDao dao = context.getBean("userDao", UserDao.class);
	...
}



DaoFactory 클래스를 @Configuration 어노테이션을 사용해 설정정보 클래스라고 선언한뒤 UserDaoDatasource 객체를 @Bean으로 설정했다. 스프링에서 제공하는 어노테이션을 활용해 간단하게 설정정보와 빈을 등록할 수 있다.

userDao() 메소드 내에서 UserDao 객체를 생성하고, setDataSource() 메소드를 사용하여 DataSource 객체를 주입했다. 이를 통해 UserDaoDataSource에 의존성을 갖게 된다.

dataSource() 메소드에서는 SimpleDriverDataSource 객체를 생성하고, 데이터베이스 연결 정보를 설정하여 반환합니다.

이후 UserDaoTest 클래스에서는 DaoFactory 클래스를 설정정보로 사용하는 스프링 컨테이너 ( ApplicationContext )를 만들고 있다.

위의 코드는 @Bean으로 생성된 userDao 스프링빈을 UserDaoTest 에서 가져와 사용하는 코드이다. UserDaoTestUserDao의 설정정보를 사용하는 애플리케이션 콘텍스트로 작동하는 것이다.




📌 XML을 활용한 스프링 컨테이너 구현


<!-- DaoFactory.xml -->  

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="userDao" class="com.example.UserDao">  
		<property name="dataSource" ref="dataSource"/>  
	</bean>
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">  <property name="driverClass" value="com.mysql.jdbc.Driver"/>  
		<property name="url" value="jdbc:mysql://localhost/user"/>
		<property name="username" value="root"/>  
		<property name="password" value="root"/>  
	</bean>
	
</beans>


위의 XML 설정 파일은 DaoFactory 클래스에서 사용한 빈 객체들을 정의하고 구성한다.

<bean> 요소는 스프링 빈을 정의하는 역할을 한다. id 속성은 빈의 식별자를 지정하고, class 속성은 해당 빈 객체의 클래스 경로를 지정한다.

<property> 요소는 빈 객체의 프로퍼티를 설정하는 역할을 한다. name 속성은 프로퍼티 이름을 지정하고, value 속성은 프로퍼티 값을 지정한다. ref 속성은 다른 빈 객체를 참조할 때 사용된다.

따라서, 위의 XML 설정 파일은 userDao 빈 객체와 dataSource 빈 객체를 정의하고, userDaodataSource 프로퍼티에 dataSource 빈 객체를 주입하는 설정을 나타낸다.





✅ 애플리케이션 컨텍스트 동작 방식

Application Context는 IOC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다. 생성정보와 관계설정에 대한 정보는 별도의 설정정보를 통해 얻는데 @Configuration 어노테이션이 달린 클래스나 XML파일에서 설정정보를 가져온다. 앞선 예제에 대한 그림으로 Application Context의 동작 방식을 설명해보겠다.

  1. ApplicationContext는 @Configuration 어노테이션이 사용된 DaoFactory 클래스를 설정정보로 지정한다.
  2. 설정정보에서 @Bean 어노테이션이 사용된 메소드의 이름을 가져와 Bean 목록을 만들어 저장해둔다.
  3. 클라이언트가 getBean() 메소드를 사용해 userDao 객체 사용을 요청한다
  4. ApplicationContext는 빈목록에서 userDao 빈을 찾고 해당 메소드를 호출해서 오브젝트를 생성한다.
  5. 생성된 userDao 빈을 클라이언트가 사용한다.



댓글남기기